﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JESAI.HttpRequestMonitor.Core.Abstracts;
using Microsoft.Extensions.Logging;

using Newtonsoft.Json;

using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using Quartz.Spi;

namespace JESAI.HttpRequestMonitor.Dashboard.API.Services.Quartz
{
    public class QuartzSchedulerService
    {
        private const string SchedulerGroup = "HttpRequestMonitor_Scheduler";

        private const string SchedulerTag = "monitor_";

        private IRequestMonitorStorage _storage;

        private IScheduler _scheduler = null;

        public IJobFactory JobFactory { get; }

        protected QuartzLogProvider QuartzLogProvider { get; }

        protected ILogger<QuartzSchedulerService> Logger { get; }

        public QuartzSchedulerService(IJobFactory jobFactory, QuartzLogProvider quartzLogProvider, ILogger<QuartzSchedulerService> logger, IRequestMonitorStorage storage)
        {
            JobFactory = jobFactory;
            QuartzLogProvider = quartzLogProvider;
            Logger = logger;
            _storage = storage;
        }

        public async Task InitAsync()
        {
            _scheduler = await StdSchedulerFactory.GetDefaultScheduler().ConfigureAwait(false) ?? throw new TypeInitializationException(nameof(QuartzSchedulerService), null);
            LogProvider.SetCurrentLogProvider(QuartzLogProvider);
            _scheduler.JobFactory = JobFactory;

            await ClearDataJobAsync();

            await InitMonitorScheduleJobModelAsync();

            await _scheduler.Start().ConfigureAwait(false);
        }

        public async Task InitMonitorScheduleJobModelAsync()
        {
            List<IMonitorScheduleJobModel> list = await _storage.GetMonitorScheduleJobs();

            if (list == null || list.Count == 0)
            {
                return;
            }

            list = list.Where(x => x.Status == 1).ToList();

            foreach (var item in list)
            {
                await ScheduleJobAsync(item);
            }
        }

        private async Task ScheduleJobAsync(IMonitorScheduleJobModel model)
        {
            var job = JobBuilder.Create<MonitorBackendJob>().
                   WithIdentity(SchedulerTag + model.Id, SchedulerGroup)
                   .SetJobData(new JobDataMap { { "job", model } }).Build();

            var trigger = TriggerBuilder.Create().WithCronSchedule(model.CronLike).Build();

            await _scheduler.ScheduleJob(job, trigger);
        }

        private async Task DeleteJobAsync(IJobDetail job)
        {
            if (_scheduler.CheckExists(job.Key).Result)
            {
                await _scheduler.PauseJob(job.Key);

                await _scheduler.DeleteJob(job.Key);
            }
        }

        public async Task UpdateMonitorScheduleJobModelAsync()
        {
            List<IMonitorScheduleJobModel> list = await _storage.GetMonitorScheduleJobs();

            if (list == null || list.Count == 0)
            {
                return;
            }

            foreach (var k in list)
            {
                var job = await _scheduler.GetJobDetail(new JobKey(SchedulerTag + k.Id, SchedulerGroup));

                if (job == null)
                {
                    if (k.Status == 1) await ScheduleJobAsync(k);
                }
                else
                {
                    if (k.Status == 0)
                    {
                        await DeleteJobAsync(job);
                    }
                    else
                    {
                        IMonitorScheduleJobModel MonitorScheduleJobModel = job.JobDataMap.Get("job") as IMonitorScheduleJobModel;

                        // 判断是否有修改，如果修改后，重置Job
                        if (JsonConvert.SerializeObject(k) != JsonConvert.SerializeObject(MonitorScheduleJobModel))
                        {
                            await DeleteJobAsync(job);
                            await ScheduleJobAsync(k);
                        }
                    }
                }
            }
        }

        public async Task ClearDataJobAsync()
        {
            var job = JobBuilder.Create<ClearReportsDataJob>().Build();

            var trigger = TriggerBuilder.Create().WithCronSchedule(SysConst.ClearDataCornLike).Build();

            await _scheduler.ScheduleJob(job, trigger);
        }
    }
}